package ms.maplestory.boot.packets;

import ms.maplestory.boot.Server;
import ms.maplestory.boot.client.User;
import ms.maplestory.boot.client.character.Char;
import ms.maplestory.boot.client.character.Ranking;
import ms.maplestory.boot.configs.BalloonConfig;
import ms.maplestory.boot.ServerConstants;
import ms.maplestory.boot.client.Account;
import ms.maplestory.boot.connection.OutPacket;
import ms.maplestory.boot.connection.opcode.OutHeader;
import ms.maplestory.boot.enums.login.*;
import ms.maplestory.boot.packets.universal.AvatarLook;
import ms.maplestory.boot.packets.universal.CharacterStat;
import ms.maplestory.boot.enums.*;
import ms.maplestory.boot.world.ChannelServer;
import ms.maplestory.boot.world.WorldServer;

import java.util.*;

public class LoginPacket {

    public static OutPacket sendConnect(byte[] siv, byte[] riv) {
        OutPacket out = new OutPacket();
        out.encodeShort(14);
        out.encodeShort(ServerConstants.VERSION);
        out.encodeString(ServerConstants.MINOR_VERSION);
        out.encodeArr(siv);
        out.encodeArr(riv);
        out.encodeByte(ServerConstants.LOCALE);
        return out;
    }

    public static OutPacket sendCheckPassword(LoginType type, Account account) {
        OutPacket out = new OutPacket(OutHeader.LP_CheckPasswordResult);
        out.encodeByte(type.getOpcode());
        int v5 = 0;
        out.encodeByte(v5);
        out.encodeInt(0);
        switch (type) {
            case Deleted://16 id has been deleted
                int v7 = 0;
                out.encodeByte(v7); //99 error pincode for 5 times 199 for 10 times 299 more than 10 times
                out.encodeLong(0);//filetime
                break;
            case TrialVersions://download full client
            case SystemError://15 trouble login in?
            case NotConnectableWorld://15 trouble login in?
            case Unknown://15 trouble login in?
            case Blocked://16 id has been deleted
            case IncorrectPassword://3 incorrect password
            case NotRegistered://20 not a reg id
            case AlreadyLoggedIn://goto title 17 id already login
            case ManyConnections://19 too many connect
            case Only://14 20 years old only
            case ImpossibleIP://21 unable to login as a master as this IP
            case WrongGateway://give yes no to open website 27 wrong gateway selected
            case Button://give yes no to open website 26 process your request now
            case VerifyAccount://33 verify email to login
            case VerifyEmail://33 verify email to login
            case WrongInfo://27 wrong gateway selected
            case Notice://40 out of service region
            default:
                break;
        }
        if (type == LoginType.Success || type == LoginType.LicenseAgreement) {
            switch (v5) {
                case 0:
                case 1: {
                    if (type == LoginType.LicenseAgreement) {
                        return out;//show license
                    }
                    out.encodeInt(0);//pBlockReason
                    int pBlockReasonIter = 0;
                    out.encodeByte(pBlockReasonIter); //pBlockReasonIter 10：选择性别
                    GameManagerFlag gameManager = GameManagerFlag.getByLevel(0);
                    byte subGradeCode = 0;
                    subGradeCode |= gameManager.getSubGradeCode();
                    out.encodeByte(gameManager.getLevel() >= GameManagerFlag.Gm.getLevel());//nGradeCode
                    out.encodeShort(subGradeCode);//nSubGradeCode
                    out.encodeByte(0);//nCountryID
                    out.encodeString("");//sNexonClubID
                    out.encodeByte(0);//nPurchaseExp
                    out.encodeByte(0);//nChatBlockReason
                    out.encodeLong(0L);//dtChatUnblockDate
                    out.encodeLong(0L);//dtRegisterDate
                    out.encodeInt(2);//nNumOfCharacter
                    if (pBlockReasonIter != 10) {
                        out.encodeByte(ServerConstants.CLOSE_PIN_CODE);
                        out.encodeByte(ServerConstants.CLOSE_PIN_CODE ? 2 : account.getPinCode() == null ? 0 : 1);
                        out.encodeLong(0L); //vParam.boolVal filetime
                    }
                }
                break;
                case 2://31 account has not been verify
                case 3://31 account has not been verify
                default://15 //15 trouble login in?
                    break;
            }
        }
        return out;
    }

    public static OutPacket sendCheckUserLimit(UserLimitType overUserLimit, int populateLevel) {
        OutPacket out = new OutPacket(OutHeader.LP_CheckUserLimitResult);
        out.encodeByte(overUserLimit.getOpcode());
        out.encodeByte(populateLevel);
        return out;
    }

    public static OutPacket sendCheckPinCode(CheckPinCodeType opcode) {
        OutPacket out = new OutPacket(OutHeader.LP_CheckPinCodeResult);
        out.encodeByte(opcode.getOpcode());
        return out;
    }

    public static OutPacket sendUpdatePinCode(boolean fail) {
        OutPacket out = new OutPacket(OutHeader.LP_UpdatePinCodeResult);
        out.encodeByte(fail);
        return out;
    }

    public static OutPacket sendViewAllChar(ViewAllCharType type, User user, int charCount, String data) {
        OutPacket out = new OutPacket(OutHeader.LP_ViewAllCharResult);
        out.encodeByte(type.getOpcode());
        switch (type) {
            case InitData:
                out.encodeByte(user.getWorldId());
                Set<Char> chars = user.getCharacters();
                out.encodeByte(chars.size());
                for (Char chr : chars) {
                    CharacterStat.encode(out, chr.getStat());
                    AvatarLook.encode(out, chr, false);
                    Ranking ranking = chr.getRanking();
                    out.encodeByte(ranking != null);
                    if (ranking != null) {
                        out.encodeInt(ranking.getWorldRank());
                        out.encodeInt(ranking.getWorldRank());
                        out.encodeInt(ranking.getJobRank());
                        out.encodeInt(ranking.getJobRankGap());
                    }

                }
                out.encodeByte(user.getSpw() == null ? 0 : 1);
                break;
            case InitCount:
                out.encodeInt(charCount);
                out.encodeInt(charCount + (3 - charCount % 3));
                break;
            case NoticeMessage:
            case DataNoticeMessage:
            case CountNoticeMessage:
                out.encodeByte(true);
                out.encodeString(data);
                break;
        }
        return out;
    }

    public static OutPacket sendWorldInformation(boolean end, WorldServer worldServer) {
        OutPacket out = new OutPacket(OutHeader.LP_WorldInformation);
        if (end) {
            out.encodeByte(-1);
        } else {
            out.encodeByte(worldServer.getId());
            out.encodeString(worldServer.getName());
            out.encodeByte(worldServer.getState());
            out.encodeString(worldServer.getEventDesc());
            out.encodeShort(worldServer.getEventEXP_WSE());
            out.encodeShort(worldServer.getEventDrop_WSE());
            out.encodeByte(worldServer.isBlockCharCreation());
            List<ChannelServer> channelServers = worldServer.getChannelServers();
            out.encodeByte(channelServers.size());
            channelServers.forEach(channel -> {
                out.encodeString(channel.getName());
                out.encodeInt(channel.getUserNo());
                out.encodeByte(channel.getWorldId());
                out.encodeByte(channel.getId());
                out.encodeByte(channel.isAdultChannel());
            });
            List<BalloonConfig> balloonList = Server.getInstance().getBalloon();
            out.encodeShort(balloonList.size());
            balloonList.forEach(balloon -> {
                out.encodeShort(balloon.getX());
                out.encodeShort(balloon.getY());
                out.encodeString(balloon.getMessage());
            });
        }
        return out;
    }

    public static OutPacket sendSelectWorld(User user, boolean onFamily) {
        OutPacket out = new OutPacket(OutHeader.LP_SelectWorldResult);
        out.encodeByte(0);
        List<Char> chars = new ArrayList<>(user.getCharacters());
        chars.sort(Comparator.comparingInt(Char::getId));
        out.encodeByte(chars.size());
        chars.forEach(chr -> {
            CharacterStat.encode(out, chr.getStat());
            AvatarLook.encode(out, chr, false);
            out.encodeByte(onFamily);
            out.encodeByte(chr.getRanking() != null);
            if (chr.getRanking() != null) {
                out.encodeInt(chr.getRanking().getWorldRank());
                out.encodeInt(chr.getRanking().getWorldRankGap());
                out.encodeInt(chr.getRanking().getJobRank());
                out.encodeInt(chr.getRanking().getJobRankGap());
            }
        });
        out.encodeByte(user.getSpw() == null ? 0 : 1);
        out.encodeInt(user.getCharSlotCount());
        out.encodeInt(user.getBuyCharCount());
        return out;
    }

    public static OutPacket sendSelectCharacter(SelectCharacterType selectCharacterType, int code, ChannelServer channelServer, int charId) {
        OutPacket out = new OutPacket(OutHeader.LP_SelectCharacterResult);
        out.encodeByte(selectCharacterType.getOpcode());
        out.encodeByte(code);
        if (selectCharacterType == SelectCharacterType.Success) {
            switch (code) {
                case 11:
                case 13:
                    if (selectCharacterType.getOpcode() != 34) {
                        out.encodeArr(ServerConstants.LOGIN_IP);
                        out.encodeShort(channelServer.getPort());
                        out.encodeInt(charId);
                        out.encodeByte(0); // AuthenCode
                        out.encodeInt(0);
                    }
                    break;
                case 1:
                case 2:
                case 3:
                case 19:
                case 25:
                case 27:
                case 28:
                default:
                    break;
            }
        }
        return out;
    }

    public static OutPacket sendCheckDuplicatedId(String name, CheckDuplicatedIdType checkDuplicatedIdType) {
        OutPacket outPacket = new OutPacket(OutHeader.LP_CheckDuplicatedIDResult);
        outPacket.encodeString(name);
        outPacket.encodeByte(checkDuplicatedIdType.getOpcode());
        return outPacket;
    }

    public static OutPacket sendCreateNewCharacter(CreateNewCharacterType type, Char chr) {
        OutPacket out = new OutPacket(OutHeader.LP_CreateNewCharacterResult);
        out.encodeByte(type.getOpcode());
        if (type == CreateNewCharacterType.Success) {
            CharacterStat.encode(out, chr.getStat());
            AvatarLook.encode(out, chr, false);
        }
        return out;
    }

    public static OutPacket sendEnableSPW(boolean engaged, EnableSPWOp enableSPWOp) {
        OutPacket out = new OutPacket(OutHeader.LP_EnableSPWResult);
        out.encodeByte(engaged);
        out.encodeByte(enableSPWOp.getOpcode());
        return out;
    }

    public static OutPacket sendLatestConnectedWorld(int worldId) {
        OutPacket out = new OutPacket(OutHeader.LP_LatestConnectedWorld);
        out.encodeInt(worldId);
        return out;
    }

    public static OutPacket sendRecommendWorldMessage(List<WorldServer> worldServers) {
        OutPacket out = new OutPacket(OutHeader.LP_RecommendWorldMessage);
        out.encodeByte(worldServers.size());
        worldServers.forEach(worldServer -> {
            out.encodeInt(worldServer.getId());
            out.encodeString(worldServer.getRecommendMsg());
        });
        return out;
    }

}
